]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
extensionSystem: Add install-from-HTTP capability
authorJasper St. Pierre <jstpierre@mecheye.net>
Wed, 24 Aug 2011 17:08:34 +0000 (13:08 -0400)
committerJasper St. Pierre <jstpierre@mecheye.net>
Wed, 24 Aug 2011 17:58:22 +0000 (13:58 -0400)
This adds a new DBus method: InstallExtensionRemote(uuid : s, url : s)

Pass it the UUID of an extension and the URL of a manifest file: the same as a
metadata.json, but with a special key, '__installer', which is an HTTP location
that points to an zip file containing the extension. The Shell will download
and use it to install the extension. In the future, the manifest file may be
used to automatically detect and install updates.

https://bugzilla.gnome.org/show_bug.cgi?id=654770

js/ui/extensionSystem.js
js/ui/shellDBus.js

index 6bd6b91775ad719880ef882443249ef3629345b7..8907a8ee8d391de7cea6e8de2572f87e54ddbac9 100644 (file)
@@ -7,6 +7,7 @@ const GLib = imports.gi.GLib;
 const Gio = imports.gi.Gio;
 const St = imports.gi.St;
 const Shell = imports.gi.Shell;
+const Soup = imports.gi.Soup;
 
 const Config = imports.misc.config;
 
@@ -26,6 +27,15 @@ const ExtensionType = {
     PER_USER: 2
 };
 
+const _httpSession = new Soup.SessionAsync();
+
+// The unfortunate state of gjs, gobject-introspection and libsoup
+// means that I have to do a hack to add a feature.
+// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
+
+if (Soup.Session.prototype.add_feature != null)
+    Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
+
 // Maps uuid -> metadata object
 const extensionMeta = {};
 // Maps uuid -> importer object (extension directory tree)
@@ -81,6 +91,79 @@ function versionCheck(required, current) {
     return false;
 }
 
+function installExtensionFromManifestURL(uuid, url) {
+    _httpSession.queue_message(
+        Soup.Message.new('GET', url),
+        function(session, message) {
+            if (message.status_code != Soup.KnownStatusCode.OK) {
+                logExtensionError(uuid, 'downloading manifest: ' + message.status_code.toString());
+                return;
+            }
+
+            let manifest;
+            try {
+                manifest = JSON.parse(message.response_body.data);
+            } catch (e) {
+                logExtensionError(uuid, 'parsing: ' + e.toString());
+                return;
+            }
+
+            if (uuid != manifest['uuid']) {
+                logExtensionError(uuid, 'manifest: manifest uuids do not match');
+                return;
+            }
+
+            installExtensionFromManifest(manifest, meta);
+        });
+}
+
+function installExtensionFromManifest(manifest, meta) {
+    let uuid = manifest['uuid'];
+    let name = manifest['name'];
+
+    let url = manifest['__installer'];
+    _httpSession.queue_message(Soup.Message.new('GET', url),
+                               function(session, message) {
+                                   gotExtensionZipFile(session, message, uuid);
+                               });
+}
+
+function gotExtensionZipFile(session, message, uuid) {
+    if (message.status_code != Soup.KnownStatusCode.OK) {
+        logExtensionError(uuid, 'downloading extension: ' + message.status_code);
+        return;
+    }
+
+    // FIXME: use a GFile mkstemp-type method once one exists
+    let fd, tmpzip;
+    try {
+        [fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
+    } catch (e) {
+        logExtensionError(uuid, 'tempfile: ' + e.toString());
+        return;
+    }
+
+    let stream = new Gio.UnixOutputStream({ fd: fd });
+    let dir = userExtensionsDir.get_child(uuid);
+    Shell.write_soup_message_to_stream(stream, message);
+    stream.close(null);
+    let [success, pid] = GLib.spawn_async(null,
+                                          ['unzip', '-uod', dir.get_path(), '--', tmpzip],
+                                          null,
+                                          GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
+                                          null);
+
+    if (!success) {
+        logExtensionError(uuid, 'extract: could not extract');
+        return;
+    }
+
+    GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
+        GLib.spawn_close_pid(pid);
+        loadExtension(dir, true, ExtensionType.PER_USER);
+    });
+}
+
 function disableExtension(uuid) {
     let meta = extensionMeta[uuid];
     if (!meta)
index ae679ad52cb6e313a2c708b7b6582782bd44124d..07f2881b9e44dbb2e1ca8fb9d2dd5229baec0a12 100644 (file)
@@ -43,6 +43,10 @@ const GnomeShellIface = {
               { name: 'DisableExtension',
                 inSignature: 's',
                 outSignature: ''
+              },
+              { name: 'InstallRemoteExtension',
+                inSignature: 's',
+                outSignature: ''
               }
              ],
     signals: [],
@@ -166,6 +170,10 @@ GnomeShell.prototype = {
         global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
     },
 
+    InstallRemoteExtension: function(uuid, url) {
+        ExtensionSystem.installExtensionFromManifestURL(uuid, url);
+    },
+
     get OverviewActive() {
         return Main.overview.visible;
     },